/**
   	gvalid - the JSR-250 wrapper for performing interface-based, primitives-supported validation.
   	
    Copyright (C) 2014  Grzegorz Gurgul grzegorz.gurgul@xanthreal.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.xanthreal.gvalid;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.StringMemberValue;

public class AnnotationAssistanceUtils {

	/**
	 * Updates a class making its field annotated with specified annotations.
	 * 
	 * @param clazz
	 * @param fieldName
	 * @return
	 * @throws Exception
	 */
	public static Class<? extends Object> addAnnotationToField(Class<?> clazz,
			String fieldName, java.lang.annotation.Annotation[] annotationsToApply)
			throws Exception {
		ClassPool pool = ClassPool.getDefault();
		CtClass cc = pool.getCtClass(clazz.getName());
		CtField fieldDescriptor = cc.getDeclaredField(fieldName);
		ClassFile ccFile = cc.getClassFile();
		ConstPool constpool = ccFile.getConstPool();
		AnnotationsAttribute attr = new AnnotationsAttribute(constpool,
				AnnotationsAttribute.visibleTag);

		for (int i = 0; i < annotationsToApply.length; i++) {
			Class<?> annotationToApplyClass = annotationsToApply[i].getClass();
			Annotation annot = new Annotation(constpool, pool.get(annotationToApplyClass
					.getCanonicalName()));
			attr.addAnnotation(annot);
		}

		fieldDescriptor.getFieldInfo().addAttribute(attr);

		Class<?> dynamiqueBeanClass = (Class<?>) cc.toClass();
		return dynamiqueBeanClass;
	}

	/**
	 * Updates a class making its method annotated with specified annotation.
	 * 
	 * @param clazz
	 * @param methodName
	 * @return
	 * @throws Exception
	 */
	public static <T> Class<T> addAnnotationToMethod(Class<T> clazz, String methodName)
			throws Exception {

		// pool creation
		ClassPool pool = ClassPool.getDefault();
		// extracting the class
		CtClass cc = pool.getCtClass(clazz.getName());
		// looking for the method to apply the annotation on
		CtMethod sayHelloMethodDescriptor = cc.getDeclaredMethod(methodName);
		// create the annotation
		ClassFile ccFile = cc.getClassFile();
		ConstPool constpool = ccFile.getConstPool();
		AnnotationsAttribute attr = new AnnotationsAttribute(constpool,
				AnnotationsAttribute.visibleTag);

		Annotation annot = new Annotation("sample.PersonneName", constpool);
		annot.addMemberValue("name",
				new StringMemberValue("World!! (dynamic annotation)", ccFile.getConstPool()));
		attr.addAnnotation(annot);

		// add the annotation to the method descriptor
		sayHelloMethodDescriptor.getMethodInfo().addAttribute(attr);

		// transform the ctClass to java class
		@SuppressWarnings("unchecked")
		Class<T> dynamiqueBeanClass = (Class<T>) cc.toClass();
		return dynamiqueBeanClass;
	}

}
